import 'dart:io';
import 'dart:ffi' as ffi;
import 'sxtwl_flutter_bindings_generated.dart';

//======================================================================================
const String _libName = 'sxtwl_c';

/// The dynamic library in which the symbols for [SxtwlFlutterBindings] can be found.
final ffi.DynamicLibrary _dylib = () {
  if (Platform.isMacOS || Platform.isIOS) {
    return ffi.DynamicLibrary.open('$_libName.framework/$_libName');
  }
  if (Platform.isAndroid || Platform.isLinux) {
    return ffi.DynamicLibrary.open('lib$_libName.so');
  }
  if (Platform.isWindows) {
    return ffi.DynamicLibrary.open('$_libName.dll');
  }
  throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}');
}();

/// The bindings to the native functions in [_dylib].
final SxtwlFlutterBindings _bindings = SxtwlFlutterBindings(_dylib);

//======================================================================================
Future<bool> init() async {
  return true;
}

//======================================================================================
class GZ implements ffi.Finalizable {
  late ffi.Pointer<ffi.Void> ptr;

  static final _finalizer = ffi.NativeFinalizer(_dylib.lookup('GZ_free'));

  GZ({int tg = 0, int dz = 0}) {
    ptr = _bindings.GZ_new(tg, dz);
    _finalizer.attach(this, ptr);
  }

  int get dz {
    return _bindings.GZ_getDz(ptr);
  }

  int get tg {
    return _bindings.GZ_getTg(ptr);
  }

  set dz(int value) {
    _bindings.GZ_setDz(ptr, value);
  }

  set tg(int value) {
    _bindings.GZ_setTg(ptr, value);
  }
}

class Day {
  late ffi.Pointer<sxtwl_Day> _dayPtr;

  Day._fromDayPtr(ffi.Pointer<sxtwl_Day> dayPtr) {
    _dayPtr = dayPtr;
  }

  Day.fromSolar({required int year, required int month, required int day}) {
    _dayPtr = _bindings.sxtwl_newDay();
    _bindings.sxtwl_fromSolar(_dayPtr, year, month, day);
  }

  Day.fromLunar(
      {required int year,
      required int month,
      required int day,
      bool isRun = false}) {
    _dayPtr = _bindings.sxtwl_newDay();
    _bindings.sxtwl_fromLunar(_dayPtr, year, month, day, isRun);
  }

  Day after({required int dayNum}) {
    var outDayPtr = _bindings.sxtwl_newDay();
    _bindings.sxtwl_after(_dayPtr, outDayPtr, dayNum);
    return Day._fromDayPtr(outDayPtr);
  }

  Day before({required int dayNum}) {
    var outDayPtr = _bindings.sxtwl_newDay();
    _bindings.sxtwl_before(_dayPtr, outDayPtr, dayNum);
    return Day._fromDayPtr(outDayPtr);
  }

  int getLunarYear({bool chineseNewYearBoundary = false}) {
    return _bindings.sxtwl_getLunarYear(_dayPtr, chineseNewYearBoundary);
  }

  int getLunarMonth() {
    return _bindings.sxtwl_getLunarMonth(_dayPtr);
  }

  int getLunarDay() {
    return _bindings.sxtwl_getLunarDay(_dayPtr);
  }

  int getSolarYear() {
    return _bindings.sxtwl_getSolarYear(_dayPtr);
  }

  int getSolarMonth() {
    return _bindings.sxtwl_getSolarMonth(_dayPtr);
  }

  int getSolarDay() {
    return _bindings.sxtwl_getSolarDay(_dayPtr);
  }

  GZ getYearGZ({bool chineseNewYearBoundary = false}) {
    var ret = GZ();
    _bindings.sxtwl_getYearGZ(ret.ptr, _dayPtr, chineseNewYearBoundary);
    return ret;
  }

  GZ getMonthGZ() {
    var ret = GZ();
    _bindings.sxtwl_getMonthGZ(ret.ptr, _dayPtr);
    return ret;
  }

  GZ getDayGZ() {
    var ret = GZ();
    _bindings.sxtwl_getDayGZ(ret.ptr, _dayPtr);
    return ret;
  }

  GZ getHourGZ({required int hour, bool isZaoWanZiShi = true}) {
    var ret = GZ();
    _bindings.sxtwl_getHourGZ(ret.ptr, _dayPtr, hour, isZaoWanZiShi);
    return ret;
  }

  bool isLunarLeap() {
    return _bindings.sxtwl_isLunarLeap(_dayPtr);
  }

  // 星期几
  int getWeek() {
    return _bindings.sxtwl_getWeek(_dayPtr);
  }

  // 处于该月的第几周
  int getWeekIndex() {
    return _bindings.sxtwl_getWeekIndex(_dayPtr);
  }

  // 获取星座
  int getConstellation() {
    return _bindings.sxtwl_getConstellation(_dayPtr);
  }

  // 是否有节气
  bool hasJieQi() {
    return _bindings.sxtwl_hasJieQi(_dayPtr);
  }

  // 获取节气索引
  int getJieQi() {
    return _bindings.sxtwl_getJieQi(_dayPtr);
  }

  double getJieQiJD() {
    return _bindings.sxtwl_getJieQiJD(_dayPtr);
  }

  dispose() {
    _bindings.sxtwl_freeDay(_dayPtr);
  }
}

//========================================================================================
JdList siZhu2Year(
    {required int yearTg,
    required int yearDz,
    required int monthTg,
    required int monthDz,
    required int dayTg,
    required int dayDz,
    required int hourTg,
    required int hourDz,
    int fromYear = 1800,
    int toYear = 2300}) {
  var jdListPtr = _bindings.JdList_new();

  return JdList().init(_bindings.sxtwl_siZhu2Year(
      jdListPtr,
      GZ(tg: yearTg, dz: yearDz).ptr,
      GZ(tg: monthTg, dz: monthDz).ptr,
      GZ(tg: dayTg, dz: dayDz).ptr,
      GZ(tg: hourTg, dz: hourDz).ptr,
      fromYear,
      toYear));
}

GZ getShiGz(
    {required int dayTg, required int hour, bool isZaoWanZiShi = true}) {
  var ret = GZ();
  _bindings.sxtwl_getShiGz(ret.ptr, dayTg, hour, isZaoWanZiShi);
  return ret;
}

// 获取一年中的阴历润月
getRunMonth({required int year}) {
  return _bindings.sxtwl_getRunMonth(year);
}

// 获取阴历月的天数
getLunarMonthNum({required int year, required int month, bool isRun = false}) {
  return _bindings.sxtwl_getLunarMonthNum(year, month, isRun);
}

SxtwlTime JD2DD({required double jd}) {
  var time = SxtwlTime();
  _bindings.sxtwl_JD2DD(time.ptr, jd);

  return time;
}

double toJD(SxtwlTime time) {
  return _bindings.sxtwl_toJD(time.ptr);
}

//========================================================================================
class JdList implements ffi.Finalizable {
  late ffi.Pointer<ffi.Void> ptr;

  static final _finalizer = ffi.NativeFinalizer(_dylib.lookup('JdList_free'));

  JdList init(ffi.Pointer<ffi.Void> ptr) {
    if (ptr == ffi.nullptr) {
      ptr = _bindings.JdList_new();
    }

    _finalizer.attach(this, ptr);
    return this;
  }

  int get num {
    return _bindings.JdList_getNum(ptr);
  }

  double indexOf(int index) {
    return _bindings.JdList_indexOf(ptr, index);
  }
}

class SxtwlTime implements ffi.Finalizable {
  late ffi.Pointer<ffi.Void> ptr;

  static final _finalizer = ffi.NativeFinalizer(_dylib.lookup('Time_free'));

  SxtwlTime(
      {int year = 0,
      int month = 0,
      int day = 0,
      double hour = 0,
      double min = 0,
      double sec = 0}) {
    ptr = _bindings.Time_new();

    this.year = year;
    this.month = month;
    this.day = day;
    this.hour = hour;
    this.min = min;
    this.sec = sec;

    _finalizer.attach(this, ptr);
  }

  int get year {
    return _bindings.Time_getYear(ptr);
  }

  set year(int value) {
    _bindings.Time_setYear(ptr, value);
  }

  int get month {
    return _bindings.Time_getMonth(ptr);
  }

  set month(int value) {
    _bindings.Time_setMonth(ptr, value);
  }

  int get day {
    return _bindings.Time_getDay(ptr);
  }

  set day(int value) {
    _bindings.Time_setDay(ptr, value);
  }

  double get hour {
    return _bindings.Time_getHour(ptr);
  }

  set hour(double value) {
    _bindings.Time_setHour(ptr, value);
  }

  double get min {
    return _bindings.Time_getMin(ptr);
  }

  set min(double value) {
    _bindings.Time_setMin(ptr, value);
  }

  double get sec {
    return _bindings.Time_getSec(ptr);
  }

  set sec(double value) {
    _bindings.Time_setSec(ptr, value);
  }
}
